using System;
using System.Resources;
using System.Drawing;
using System.Collections;
using System.Reflection;
using System.ComponentModel;
using System.Text;
using System.IO;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using System.Xml;

namespace HIPAA_Pipelines
{

    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    [System.Runtime.InteropServices.Guid("9d0e4103-4cce-4536-83fa-4a5040674ad6")]
    public class MessageUnenrichmentComponent :
        IBaseComponent,
        Microsoft.BizTalk.Component.Interop.IComponent,
        Microsoft.BizTalk.Component.Interop.IPersistPropertyBag,
        IComponentUI
    {
        static ResourceManager resManager = new ResourceManager("HIPAA_Pipelines.MessageUnenrichmentComponent", Assembly.GetExecutingAssembly());
        

        // Component information
        #region IBaseComponent
        public string Name { get { return "EDI Message Unenrichment Component"; } }
        public string Version { get { return "1.0"; } }
        public string Description { get { return "Message Unenrichment of EDI Envelope Information"; } }
        public IntPtr Icon
        {
            get
            {
                return new System.IntPtr();
            }
        }
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        //private System.Resources.ResourceManager resourceManager =
        //    new System.Resources.ResourceManager("MessageEnrichment", Assembly.GetExecutingAssembly());

        // Property: New Root Name
        private string _rootName;
        [
        DisplayName("Root Unenrichment Element Seed"),
        Description(@"This data forms the first half of the input document RootNode.  Example is  X12EnrichedMessage_X12_00401_270 which will become X12_00401_270.  This information should be the X12EnrichedMessage section of the message")
        ]
        public string RootNameSeed
        {
            get { return _rootName; }
            set { _rootName = value; }
        }

        // Property: New Root Namespace
        private string _rootNamespace;
        [
        DisplayName("Root Element Namespace"),
        Description(@"This forms root namespace for the new document type.")
        ]
        public string RootNamespaceSeed
        {
            get { return _rootNamespace; }
            set { _rootNamespace = value; }
        }

        private string _payerID;
        [
        DisplayName("Promote PayerID? (Yes, No)"),
        Description(@"Values:Yes, No")
        ]
        public string P_PayerID
        {
            get { return _payerID; }
            set { _payerID = value; }
        }

        private string _originalISA;
        [
        DisplayName("Promote Original ISA Segment? (Yes, No)"),
        Description(@"Values:Yes, No")
        ]
        public string P_OriginalISASegment
        {
            get { return _originalISA; }
            set { _originalISA = value; }
        }

        private string _originalGS;
        [
        DisplayName("Promote Original GS Segment? (Yes, No)"),
        Description(@"Values:Yes, No")
        ]
        public string P_OriginalGSSegment
        {
            get { return _originalGS; }
            set { _originalGS = value; }
        }
        
        private string _originalST02;
        [
        DisplayName("Promote Original ST02 Value? (Yes, No)"),
        Description(@"Values:Yes, No")
        ]
        public string P_OriginalST02
        {
            get { return _originalST02; }
            set { _originalST02 = value; }
        }

        #region IPersistPropertyBag Members

        public void InitNew()
        {
        }

        public void GetClassID(out Guid classID)
        {
            classID = new Guid("48BEC85A-20EE-40ad-BFD0-319B59A0DDBC");
        }

        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            string val = (string)ReadPropertyBag(propertyBag, "RootNameSeed");
            if (val != null) RootNameSeed = val;

            val = (string)ReadPropertyBag(propertyBag, "RootNameSpaceSeed");
            if (val != null) RootNamespaceSeed = val;

            val = (string)ReadPropertyBag(propertyBag, "P_PayerID");
            if (val != null) P_PayerID = val;

            val = (string)ReadPropertyBag(propertyBag, "P_OriginalISASegment");
            if (val != null) P_OriginalISASegment = val;

            val = (string)ReadPropertyBag(propertyBag, "P_OriginalGSSegment");
            if (val != null) P_OriginalGSSegment = val;
            
            val = (string)ReadPropertyBag(propertyBag, "P_OriginalST02");
            if (val != null) P_OriginalST02 = val;

        }
        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            object val = (object)RootNameSeed;
            WritePropertyBag(propertyBag, "RootNameSeed", val);

            val = (object)RootNamespaceSeed;
            WritePropertyBag(propertyBag, "RootNamespaceSeed", val);

            val = (object)P_PayerID;
            WritePropertyBag(propertyBag, "P_PayerID", val);

            val = (object)P_OriginalISASegment;
            WritePropertyBag(propertyBag, "P_OriginalISASegment", val);

            val = (object)P_OriginalGSSegment;
            WritePropertyBag(propertyBag, "P_OriginalGSSegment", val);

            val = (object)P_OriginalST02;
            WritePropertyBag(propertyBag, "P_OriginalST02", val);

        }

        #endregion

        #region IComponent Members
        /// <summary>
        /// This method will take a document with a structure of X12EnrichedMessage_MSGTYPE (X12_00401_270 for example)
        /// And return a simple EDI message with just the unenriched data.
        /// </summary>
        /// <param name="pContext"></param>
        /// <param name="pInMsg"></param>
        /// <returns></returns>
        public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            //This method should be reworked for effeciency and flow.  This is a first pass on the logic and not designed for performance.
            //Basically we need to check the message context for the message type.  If we dont find a message type then it is a generic message
            //We then try to go ahead and parse the inbound document for un-enrichment.

            string systemPropertiesNamespace = @"http://schemas.microsoft.com/BizTalk/2003/system-properties";
            //Get the promoted properties
            IBaseMessageContext context = pInMsg.Context;

            //Figure out if it is a generated Ack
            string msgType = context.Read("MessageType", systemPropertiesNamespace) as string;
            
            //The inbound message could possibly be a generic message type of XmlDocument
            //We therefore need to test to see if we can even determine the message type.
            //If not then we need to try and examine the namespace table in the document to determine the types
            if (msgType == null)
            {
                //Try and read in the message to determine the type
                IBaseMessagePart bodyPart = pInMsg.BodyPart;
                if (bodyPart != null)
                {
                    Stream originalStream = bodyPart.GetOriginalDataStream();
                    if (originalStream != null)
                    {
                        //Load up the inbound unenriched EDI Document
                        XmlDocument _originalDocument = new XmlDocument();
                        _originalDocument.Load(originalStream);
                        
                        //Look to find the RootNameSeed in the document.  If found then mark the position and demark from there
                        if (_originalDocument.FirstChild.Name.Contains(RootNameSeed))
                        {
                            //It does contain the seed.  Lets try to pull out the original EDI

                            //Unenrich the document
                            XmlNode _node = _originalDocument.SelectSingleNode(@"/*[local-name()='" + _originalDocument.FirstChild.LocalName + "']/*[local-name()='TransactionSet']");
                            XmlDocument _retDoc = new XmlDocument();
                            _retDoc.LoadXml(_node.InnerXml);

                            string Message_ST01 = context.Read("ST01", "http://schemas.microsoft.com/Edi/PropertySchema") as string;


                            AddContext(context, Message_ST01, _retDoc);

                            //Create MessageType
                            string messageType = RootNamespaceSeed + "#" + _retDoc.FirstChild.LocalName;

                            byte[] outBytes = System.Text.Encoding.ASCII.GetBytes(_retDoc.InnerXml.ToString());
                            MemoryStream memStream = new MemoryStream();
                            memStream.Write(outBytes, 0, outBytes.Length);
                            memStream.Position = 0;
                            bodyPart.Data = memStream;
                            pContext.ResourceTracker.AddResource(memStream);

                            pInMsg.Context.Promote("MessageType", systemPropertiesNamespace, messageType);
                            return pInMsg;
                        }
                        else
                        {
                            //hmmmm
                            Console.WriteLine("Strangeness");
                        }
                    }
                }
            }
            else
            {
                //Split on message type
                char[] typeSplit = new char[] { '#' };
                string[] _splitMsgType = msgType.Split(typeSplit);
                //In an enriched document this will typcially look like this: "http://schemas.microsoft.com/BizTalk/EDI/X12/2006/EnrichedMessage_X12_00401_270#X12EnrichedMessage_X12_00401_270"
                //We need to unenrich and return a document of "NewRootNamespace#MSGTYPE(X12_00401_270)
                //To parse the MSGType we need to turn X12EnrichedMessage_X12_00401_270 into X12_00401_270.
                //In the RootNameSeed the customer is providing the information of X12EnrichedMessage.  We just need to look for the last index of this and remove the underscore the Enrichment pipeline uses
                string rootNS = _splitMsgType[0];
                string msgTypeInfo = _splitMsgType[1];

                switch (msgTypeInfo)
                {
                    case "X12_00401_997":
                        //We need to exit here
                        return pInMsg;
                        break;
                    case "X12_TA1_Root":
                        //We need to exit here
                        return pInMsg;
                        break;
                    default:
                        break;
                }


                //This takes the entire string and removes the seed value plus the underscore then returns the remainder
                string unEnrichedMsgType = msgTypeInfo.Substring(RootNameSeed.Length + 1, msgTypeInfo.Length - RootNameSeed.Length - 1);

                IBaseMessagePart bodyPart = pInMsg.BodyPart;

                string messageType = "";

                if (bodyPart != null)
                {
                    Stream originalStream = bodyPart.GetOriginalDataStream();
                    if (originalStream != null)
                    {
                        //Load up the inbound unenriched EDI Document
                        XmlDocument _originalDocument = new XmlDocument();
                        _originalDocument.Load(originalStream);

                        //Unenrich the document
                        XmlNode _node = _originalDocument.SelectSingleNode(@"/*[local-name()='" + msgTypeInfo + "']/*[local-name()='TransactionSet']");
                        XmlDocument _retDoc = new XmlDocument();
                        _retDoc.LoadXml(_node.InnerXml);

                        //Create MessageType
                        messageType = RootNamespaceSeed + "#" + unEnrichedMsgType;

                        string Message_ST01 = context.Read("ST01", "http://schemas.microsoft.com/Edi/PropertySchema") as string;

                        //Add Context
                        AddContext(context, Message_ST01, _retDoc);

                        byte[] outBytes = System.Text.Encoding.ASCII.GetBytes(_retDoc.InnerXml.ToString());
                        MemoryStream memStream = new MemoryStream();
                        memStream.Write(outBytes, 0, outBytes.Length);
                        memStream.Position = 0;
                        bodyPart.Data = memStream;
                        pContext.ResourceTracker.AddResource(memStream);
                    }
                }
                pInMsg.Context.Promote("MessageType", systemPropertiesNamespace, messageType);
                return pInMsg;
            }
            //We should not reach this section however if we do we want to return the original message.  Since we looked at the messagebody however
            //we need to create a new instance of the outbound type
            Stream originalStreamToReturn = pInMsg.BodyPart.GetOriginalDataStream();
            XmlDocument _returnDoc = new XmlDocument();
            _returnDoc.Load(originalStreamToReturn);


            byte[] outBytes2 = System.Text.Encoding.ASCII.GetBytes(_returnDoc.InnerXml.ToString());
            MemoryStream memStream2 = new MemoryStream();
            memStream2.Write(outBytes2, 0, outBytes2.Length);
            memStream2.Position = 0;
            pInMsg.BodyPart.Data = memStream2;
            pContext.ResourceTracker.AddResource(memStream2);

            return pInMsg;
        }
        #endregion
        #region Helper Methods

        /// <summary>
        /// Reads property value from property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <returns>Value of the property.</returns>
        private static object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
            try
            {
                pb.Read(propName, out val, 0);
            }

            catch (ArgumentException)
            {
                return val;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(ex.Message);
            }
            return val;
        }

        /// <summary>
        /// Writes property values into a property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <param name="val">Value of property.</param>
        private static void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (Exception ex)
            {
                throw new ApplicationException(ex.Message);
            }
        }

        private void AddContext(IBaseMessageContext context, string MessageType, XmlDocument document)
        {
            //Do we need to promote any properties?
            string XPATH_to_Payer = "";
            //The XPATH should be against the X12 Schema and not the enriched schema
            if (P_PayerID != null && P_PayerID.ToLower() == "yes")
            {
                //Determine the PayerID based upon MessageType - This should read things like BCBSF or HUMANA etc...
                switch (MessageType)
                {
                    case "837":
                        XPATH_to_Payer = @"/*[local-name()='X12_00401_837_I' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='TS837Q3_2000A_Loop' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='TS837Q3_2000B_Loop' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='TS837Q3_2010BC_Loop' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='NM1_PayerName_TS837Q3_2010BC' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='NM109__PayerIdentifier' and namespace-uri()='']";
                        break;
                    case "270":
                        XPATH_to_Payer = @"/*[local-name()='X12_00401_270' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='TS270A1_2000A_Loop' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='TS270A1_2100A_Loop' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='NM1_InformationSourceName_TS270A1_2100A' and namespace-uri()='http://schemas.microsoft.com/BizTalk/EDI/X12/2006']/*[local-name()='NM109__InformationSourcePrimaryIdentifier' and namespace-uri()='']";
                        break;
                    case "276":
                        XPATH_to_Payer = "";
                        break;
                }


                string PayerID = "";
                //Try to get the PayerID
                try
                {
                    PayerID = document.SelectSingleNode(XPATH_to_Payer).InnerText;
                }
                catch
                {
                    //Do nothing
                }
                context.Write("PayerID", "http://schemas.microsoft.com/Edi/PropertySchema", PayerID);
            }
            if (this.P_OriginalISASegment != null && this.P_OriginalISASegment.ToLower() == "yes")
            {
                this.P_OriginalISASegment = context.Read("ISA_Segment", "http://schemas.microsoft.com/Edi/PropertySchema") as string;
                context.Write("OriginalISASegment", "http://schemas.microsoft.com/Edi/PropertySchema", this.P_OriginalISASegment);
            }
            if (this.P_OriginalGSSegment != null && this.P_OriginalGSSegment.ToLower() == "yes")
            {
                this.P_OriginalGSSegment = context.Read("GS_Segment", "http://schemas.microsoft.com/Edi/PropertySchema") as string;
                context.Write("OriginalGSSegment", "http://schemas.microsoft.com/Edi/PropertySchema", this.P_OriginalGSSegment);
            }
            if (this.P_OriginalST02 != null && this.P_OriginalST02.ToLower() == "yes")
            {
                this.P_OriginalST02 = context.Read("ST02", "http://schemas.microsoft.com/Edi/PropertySchema") as string;
                context.Write("OriginalST02", "http://schemas.microsoft.com/Edi/PropertySchema", this.P_OriginalST02);
            }
        }
        #endregion
    }
}

